library(dplyr)
library(ggplot2)
library(stringr)

Load in gene counts for Plate 01 and format the names

AIH_filtered_genecounts <- read.delim("~/Documents/Informatics/AIH/AIH_NovaSeq_Data/AIH_100218_genecounts/project-aih_novaseq_host-gene-counts (1)/concat_gene_count_tables/aih_plate01_filt-85-99-9-90_dashed_gene_counts_final.tsv", row.names = 1, stringsAsFactors = TRUE)
AIH_unfiltered_genecounts <- read.delim("~/Documents/Informatics/AIH/AIH_NovaSeq_Data/AIH_100218_genecounts/project-aih_novaseq_host-gene-counts (1)/concat_gene_count_tables/aih_full_unfiltered_dashed_gene_counts_final.tsv", row.names = 1, stringsAsFactors = TRUE)
totalreads <- data.frame( DASHedtotalreads = colSums(AIH_filtered_genecounts[,1:55]), noDASHtotalreads = colSums(AIH_unfiltered_genecounts[,1:55]), row.names = colnames(AIH_filtered_genecounts[,1:55]))
rownames(totalreads) <- str_extract(rownames(totalreads), pattern = "aasld[.|_]\\d+")
AIH_filtered_genecounts <- AIH_filtered_genecounts[5:nrow(AIH_filtered_genecounts),1:55]
AIH_unfiltered_genecounts <- AIH_unfiltered_genecounts[5:nrow(AIH_unfiltered_genecounts),1:55]
genenames<- sapply( strsplit( rownames(AIH_filtered_genecounts), split="\\." ), "[", 1 )
rownames(AIH_filtered_genecounts) <- genenames
genenames<- sapply( strsplit( rownames(AIH_unfiltered_genecounts), split="\\." ), "[", 1 )
rownames(AIH_unfiltered_genecounts) <- genenames
colnames(AIH_filtered_genecounts) <- str_extract(colnames(AIH_filtered_genecounts), pattern = "aasld[.|_]\\d+")
colnames(AIH_unfiltered_genecounts) <- str_extract(colnames(AIH_unfiltered_genecounts), pattern = "aasld[.|_]\\d+")

Translate gene names and pull the biotype and the transcript length

library(biomaRt)
#ensemble use HG38 as a reference
listMarts(host="www.ensembl.org")
mart = useMart(biomart="ENSEMBL_MART_ENSEMBL",dataset="hsapiens_gene_ensembl", host="www.ensembl.org", ensemblRedirect = FALSE)
genemap <- getBM( attributes = c("ensembl_gene_id", "entrezgene", "hgnc_symbol", "gene_biotype", "description", "transcript_length"),
                  filters = "ensembl_gene_id",
                  values = genenames,
                  mart)

#View(listAttributes(mart))

Create a gene attributes table

#create an index in which you are rearranging ensmbl gene ids from genemap into same order as they are in genenames
idx <- match(genenames, genemap$ensembl_gene_id )
#create new dataset which binds gene attributes
entrez <- genemap$entrezgene[ idx ]
hgnc_symbol <- genemap$hgnc_symbol[ idx ]
description <- genemap$description[ idx ]
gene_biotype <- genemap$gene_biotype[ idx ]
ensembl <- genemap$ensembl_gene_id[ idx ]
transcript_length <- genemap$transcript_length[ idx ]
ga<-cbind(hgnc_symbol,description,gene_biotype,ensembl,entrez, transcript_length)
#make ga into a data frame from a matrix
ga<-as.data.frame(ga)
ga$gene_biotype<-as.character(ga$gene_biotype)
unique(ga$gene_biotype)
 [1] "transcribed_unprocessed_pseudogene" "unprocessed_pseudogene"            
 [3] "miRNA"                              "lincRNA"                           
 [5] NA                                   "protein_coding"                    
 [7] "processed_pseudogene"               "antisense"                         
 [9] "processed_transcript"               "snRNA"                             
[11] "transcribed_processed_pseudogene"   "sense_intronic"                    
[13] "misc_RNA"                           "TEC"                               
[15] "transcribed_unitary_pseudogene"     "snoRNA"                            
[17] "scaRNA"                             "rRNA_pseudogene"                   
[19] "3prime_overlapping_ncRNA"           "polymorphic_pseudogene"            
[21] "unitary_pseudogene"                 "sense_overlapping"                 
[23] "pseudogene"                         "bidirectional_promoter_lncRNA"     
[25] "rRNA"                               "IG_V_pseudogene"                   
[27] "scRNA"                              "IG_V_gene"                         
[29] "IG_C_gene"                          "IG_J_gene"                         
[31] "ribozyme"                           "translated_processed_pseudogene"   
[33] "vaultRNA"                           "TR_C_gene"                         
[35] "TR_J_gene"                          "TR_V_gene"                         
[37] "TR_V_pseudogene"                    "TR_D_gene"                         
[39] "IG_C_pseudogene"                    "sRNA"                              
[41] "macro_lncRNA"                       "TR_J_pseudogene"                   
[43] "IG_J_pseudogene"                    "IG_D_gene"                         
[45] "Mt_tRNA"                            "Mt_rRNA"                           
#View(ga %>% filter(gene_biotype=="miRNA"))
#View(ga %>% filter(gene_biotype=="Mt_rRNA"))
#View(ga %>% filter(gene_biotype=="rRNA"))
#View(ga %>% filter(gene_biotype=="Mt_rRNA"))
#View(ga %>% filter(ensembl=="ENSG00000266658"))
#View(ga %>% filter(hgnc_symbol=="RNA45S5"))

Calculate the RPKM using EdgeR

#pc = protein coding
pc<-ga[ga$gene_biotype=="protein_coding",]
pc<-pc[!(is.na(pc$ensembl)),]
head(pc)
#make an index for protein coding genes and create new gene count tables
idxpc<- match(pc$ensembl,rownames(AIH_filtered_genecounts))
genecountspc_DASHed<-AIH_filtered_genecounts[idxpc,]
rownames(genecountspc_DASHed)<-make.unique(as.character(pc$hgnc_symbol))
idxpc<- match(pc$ensembl,rownames(AIH_unfiltered_genecounts))
genecountspc_noDASH<-AIH_unfiltered_genecounts[idxpc,]
rownames(genecountspc_noDASH)<-make.unique(as.character(pc$hgnc_symbol))
#take a subset only
genecountspc_DASHed_subset <- genecountspc_DASHed[,1:9]
genecountspc_noDASH_subset <- genecountspc_noDASH[,1:9]
#genecountspc_DASHed_subset$gene <- rownames(genecountspc_DASHed_subset)
#genecountspc_noDASH_subset$gene <- rownames(genecountspc_noDASH_subset)
library(edgeR)
dgeDASHed <- DGEList(counts = genecountspc_DASHed_subset, lib.size = colSums(genecountspc_DASHed_subset), genes = pc)
dgeDASHedRPKM <- rpkm(dgeDASHed,  gene.length = as.numeric(dgeDASHed$genes$transcript_length), log = FALSE, normalized.lib.sizes = TRUE)
dgenoDASH <- DGEList(counts = genecountspc_noDASH_subset, lib.size = colSums(genecountspc_noDASH_subset), genes = pc)
dgenoDASHRPKM <- rpkm(dgenoDASH,  gene.length = as.numeric(dgenoDASH$genes$transcript_length), log = FALSE, normalized.lib.sizes = TRUE)
#merge dataframes
library(reshape2)
dgeDASHedRPKM_melt <- as.data.frame(melt(dgeDASHedRPKM, varnames = c("gene", "sample_id"), value.name = "DASHed_gene_count" ))
dgenoDASHRPKM_melt <- as.data.frame(melt(dgenoDASHRPKM, varnames = c("gene","sample_id"), value.name = "noDASH_gene_count" ))
genecounts_full_RPKM <- dplyr::left_join(dgenoDASHRPKM_melt, dgeDASHedRPKM_melt, by = c("gene", "sample_id"))
head(genecounts_full_RPKM)

Calculate the RPKM by hand

#pc = protein coding
pc<-ga[ga$gene_biotype=="protein_coding",]
pc<-pc[!(is.na(pc$ensembl)),]
head(pc)
#make an index for protein coding genes and create new gene count tables
idxpc<- match(pc$ensembl,rownames(AIH_filtered_genecounts))
genecountspc_DASHed<-AIH_filtered_genecounts[idxpc,]
rownames(genecountspc_DASHed)<-make.unique(as.character(pc$hgnc_symbol))
idxpc<- match(pc$ensembl,rownames(AIH_unfiltered_genecounts))
genecountspc_noDASH<-AIH_unfiltered_genecounts[idxpc,]
rownames(genecountspc_noDASH)<-make.unique(as.character(pc$hgnc_symbol))
#take a subset only
genecountspc_DASHed_subset <- genecountspc_DASHed[,1:9]
genecountspc_noDASH_subset <- genecountspc_noDASH[,1:9]
genecountspc_DASHed_subset$gene <- rownames(genecountspc_DASHed_subset)
genecountspc_noDASH_subset$gene <- rownames(genecountspc_noDASH_subset)
genecountspc_DASHed_subset$transcript_length <- pc$transcript_length
genecountspc_noDASH_subset$transcript_length <- pc$transcript_length
genecountspc_DASHed_subset_melt <- melt(genecountspc_DASHed_subset, variable.name = "sample_id", value.name = "DASHed_gene_count" )
Using gene, transcript_length as id variables
genecountspc_noDASH_subset_melt <- melt(genecountspc_noDASH_subset, variable.name = "sample_id", value.name = "noDASH_gene_count" )
Using gene, transcript_length as id variables
#join the two dataframes together
genecounts_full <- dplyr::left_join(genecountspc_DASHed_subset_melt, genecountspc_noDASH_subset_melt, by = c("gene", "sample_id", "transcript_length"))
#calculate the RPKM for each DASHed and unDASHed genecount
genecounts_full_RPKM <- genecounts_full %>% dplyr::group_by(sample_id) %>% dplyr::mutate(DASHedRPKM = DASHed_gene_count*1000000*1000/(sum(DASHed_gene_count)*as.numeric(transcript_length)))  %>% dplyr::mutate(noDASHRPKM = noDASH_gene_count*1000000*1000/(sum(noDASH_gene_count)*as.numeric(transcript_length)))

Calculating RPKM using the full total reads value (from the ReadsPerGene.tab file)

#pc = protein coding
pc<-ga[ga$gene_biotype=="protein_coding",]
pc<-pc[!(is.na(pc$ensembl)),]
head(pc)
#make an index for protein coding genes and create new gene count tables
idxpc<- match(pc$ensembl,rownames(AIH_filtered_genecounts))
genecountspc_DASHed<-AIH_filtered_genecounts[idxpc,]
rownames(genecountspc_DASHed)<-make.unique(as.character(pc$hgnc_symbol))
idxpc<- match(pc$ensembl,rownames(AIH_unfiltered_genecounts))
genecountspc_noDASH<-AIH_unfiltered_genecounts[idxpc,]
rownames(genecountspc_noDASH)<-make.unique(as.character(pc$hgnc_symbol))
#take a subset only
genecountspc_DASHed_subset <- genecountspc_DASHed[,1:9]
genecountspc_noDASH_subset <- genecountspc_noDASH[,1:9]
genecountspc_DASHed_subset$gene <- rownames(genecountspc_DASHed_subset)
genecountspc_noDASH_subset$gene <- rownames(genecountspc_noDASH_subset)
genecountspc_DASHed_subset$transcript_length <- pc$transcript_length
genecountspc_noDASH_subset$transcript_length <- pc$transcript_length
genecountspc_DASHed_subset_melt <- melt(genecountspc_DASHed_subset, variable.name = "sample_id", value.name = "DASHed_gene_count" )
Using gene, transcript_length as id variables
genecountspc_noDASH_subset_melt <- melt(genecountspc_noDASH_subset, variable.name = "sample_id", value.name = "noDASH_gene_count" )
Using gene, transcript_length as id variables
#join the two dataframes together
genecounts_full <- dplyr::left_join(genecountspc_DASHed_subset_melt, genecountspc_noDASH_subset_melt, by = c("gene", "sample_id", "transcript_length"))
#add the total reads (before filtering protein coding) and calculate the RPKM
genecounts_full_RPKM <- genecounts_full %>% dplyr::group_by(sample_id) %>% dplyr::mutate(DASHedtotalreads = totalreads[sample_id,"DASHedtotalreads"]) %>% dplyr::mutate(noDASHtotalreads = totalreads[sample_id,"noDASHtotalreads"]) %>% dplyr::mutate(DASHedRPKM = DASHed_gene_count*1000000*1000/(DASHedtotalreads*as.numeric(transcript_length)))  %>% dplyr::mutate(noDASHRPKM = noDASH_gene_count*1000000*1000/(noDASHtotalreads*as.numeric(transcript_length)))
#create a summary table
genecounts_full_RPKM_summary <- genecounts_full_RPKM %>% dplyr::group_by(sample_id) %>% dplyr::summarise(r2 = signif(summary(lm(DASHedRPKM ~ noDASHRPKM))$adj.r.squared, digits = 5), slope = signif(lm(DASHedRPKM ~ noDASHRPKM)$coef[[2]], digits = 3), intercept = signif(lm(DASHedRPKM ~ noDASHRPKM)$coef[[1]], digits = 3))
#create facet labels
names<- lapply(genecounts_full_RPKM_summary, as.character)
facet_titles <- c()
for (i in 1:nrow(genecounts_full_RPKM_summary)) {
  facet_titles[i] <- paste(names$sample_id[i],"\nR2 =", names$r2[i],"  y = ", names$slope[i], "x + ", names$intercept[i])
}
facet_titles
[1] "aasld_001 \nR2 = 0.99993   y =  0.994 x +  0.00498"
[2] "aasld_002 \nR2 = 0.99978   y =  1 x +  -0.00961"   
[3] "aasld_003 \nR2 = 0.99975   y =  1 x +  -0.0321"    
[4] "aasld_004 \nR2 = 0.9998   y =  0.996 x +  0.0447"  
[5] "aasld_005 \nR2 = 0.99973   y =  0.989 x +  0.0392" 
[6] "aasld_006 \nR2 = 0.99994   y =  1 x +  -0.042"     
[7] "aasld_007 \nR2 = 0.99962   y =  0.991 x +  -0.011" 
[8] "aasld_008 \nR2 = 0.99982   y =  0.996 x +  -0.028" 
[9] "aasld_009 \nR2 = 0.99981   y =  0.989 x +  0.0661" 
facet_labeller <- function(variable,value){
  return(facet_titles[value])
}
colnames(genecounts_full_RPKM)
[1] "gene"              "transcript_length" "sample_id"         "DASHed_gene_count"
[5] "noDASH_gene_count" "DASHedtotalreads"  "noDASHtotalreads"  "DASHedRPKM"       
[9] "noDASHRPKM"       
ggplot(data = genecounts_full_RPKM, aes(x= noDASHRPKM, y = DASHedRPKM)) + geom_point(color = "darkgreen", alpha = 0.5) + facet_wrap(~sample_id, labeller = facet_labeller) + stat_smooth(method = "lm", color = "red") + xlab ("unfiltered (RPKM/FPKM)") + ylab ("filt-85-99.9-90 gene counts (RPKM/FPKM)") + scale_x_log10() + scale_y_log10()
The labeller API has been updated. Labellers taking `variable`and `value` arguments are now deprecated. See labellers documentation.

Filter out RPKM with 0 values

#create filtered table
genecounts_full_RPKM_filtered <- genecounts_full_RPKM  %>% group_by(sample_id, gene) %>% filter(DASHedRPKM > 0 && noDASHRPKM > 0)
#create a summary table
genecounts_full_RPKM_summary_filtered <- genecounts_full_RPKM_filtered %>% dplyr::group_by(sample_id) %>% dplyr::summarise(r2 = signif(summary(lm(DASHedRPKM ~ noDASHRPKM))$adj.r.squared, digits = 5), slope = signif(lm(DASHedRPKM ~ noDASHRPKM)$coef[[2]], digits = 3), intercept = signif(lm(DASHedRPKM ~ noDASHRPKM)$coef[[1]], digits = 3))
#create facet labels
names<- lapply(genecounts_full_RPKM_summary_filtered, as.character)
for (i in 1:nrow(genecounts_full_RPKM_summary_filtered)) {
  facet_titles[i] <- paste(names$sample_id[i],"\nR2 =", names$r2[i],"  y = ", names$slope[i], "x + ", names$intercept[i])
}
facet_titles
[1] "aasld_001 \nR2 = 0.99993   y =  0.994 x +  0.00689"
[2] "aasld_002 \nR2 = 0.99978   y =  1 x +  -0.0137"    
[3] "aasld_003 \nR2 = 0.99975   y =  1 x +  -0.047"     
[4] "aasld_004 \nR2 = 0.9998   y =  0.996 x +  0.0567"  
[5] "aasld_005 \nR2 = 0.99973   y =  0.989 x +  0.0545" 
[6] "aasld_006 \nR2 = 0.99994   y =  1 x +  -0.0632"    
[7] "aasld_007 \nR2 = 0.99962   y =  0.991 x +  -0.0161"
[8] "aasld_008 \nR2 = 0.99982   y =  0.996 x +  -0.0386"
[9] "aasld_009 \nR2 = 0.99981   y =  0.989 x +  0.0948" 
facet_labeller <- function(variable,value){
  return(facet_titles[value])
}
View(genecounts_full_RPKM_filtered)
ggplot(data = genecounts_full_RPKM_filtered, aes(x= noDASHRPKM, y = DASHedRPKM)) + geom_point(color = "darkgreen", alpha = 0.5) + facet_wrap(~sample_id, labeller = facet_labeller) + stat_smooth(method = "lm", color = "red") + xlab ("unfiltered (RPKM/FPKM)") + ylab ("filt-85-99.9-90 gene counts (RPKM/FPKM)") + scale_x_log10() + scale_y_log10()
The labeller API has been updated. Labellers taking `variable`and `value` arguments are now deprecated. See labellers documentation.

LS0tCnRpdGxlOiAiR2VuZSBDb3VudHMgREFTSCB2cyBub0RBU0giCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KYGBge3J9CmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShzdHJpbmdyKQpgYGAKCkxvYWQgaW4gZ2VuZSBjb3VudHMgZm9yIFBsYXRlIDAxIGFuZCBmb3JtYXQgdGhlIG5hbWVzCgpgYGB7cn0KQUlIX2ZpbHRlcmVkX2dlbmVjb3VudHMgPC0gcmVhZC5kZWxpbSgifi9Eb2N1bWVudHMvSW5mb3JtYXRpY3MvQUlIL0FJSF9Ob3ZhU2VxX0RhdGEvQUlIXzEwMDIxOF9nZW5lY291bnRzL3Byb2plY3QtYWloX25vdmFzZXFfaG9zdC1nZW5lLWNvdW50cyAoMSkvY29uY2F0X2dlbmVfY291bnRfdGFibGVzL2FpaF9wbGF0ZTAxX2ZpbHQtODUtOTktOS05MF9kYXNoZWRfZ2VuZV9jb3VudHNfZmluYWwudHN2Iiwgcm93Lm5hbWVzID0gMSwgc3RyaW5nc0FzRmFjdG9ycyA9IFRSVUUpCkFJSF91bmZpbHRlcmVkX2dlbmVjb3VudHMgPC0gcmVhZC5kZWxpbSgifi9Eb2N1bWVudHMvSW5mb3JtYXRpY3MvQUlIL0FJSF9Ob3ZhU2VxX0RhdGEvQUlIXzEwMDIxOF9nZW5lY291bnRzL3Byb2plY3QtYWloX25vdmFzZXFfaG9zdC1nZW5lLWNvdW50cyAoMSkvY29uY2F0X2dlbmVfY291bnRfdGFibGVzL2FpaF9mdWxsX3VuZmlsdGVyZWRfZGFzaGVkX2dlbmVfY291bnRzX2ZpbmFsLnRzdiIsIHJvdy5uYW1lcyA9IDEsIHN0cmluZ3NBc0ZhY3RvcnMgPSBUUlVFKQp0b3RhbHJlYWRzIDwtIGRhdGEuZnJhbWUoIERBU0hlZHRvdGFscmVhZHMgPSBjb2xTdW1zKEFJSF9maWx0ZXJlZF9nZW5lY291bnRzWywxOjU1XSksIG5vREFTSHRvdGFscmVhZHMgPSBjb2xTdW1zKEFJSF91bmZpbHRlcmVkX2dlbmVjb3VudHNbLDE6NTVdKSwgcm93Lm5hbWVzID0gY29sbmFtZXMoQUlIX2ZpbHRlcmVkX2dlbmVjb3VudHNbLDE6NTVdKSkKcm93bmFtZXModG90YWxyZWFkcykgPC0gc3RyX2V4dHJhY3Qocm93bmFtZXModG90YWxyZWFkcyksIHBhdHRlcm4gPSAiYWFzbGRbLnxfXVxcZCsiKQoKCkFJSF9maWx0ZXJlZF9nZW5lY291bnRzIDwtIEFJSF9maWx0ZXJlZF9nZW5lY291bnRzWzU6bnJvdyhBSUhfZmlsdGVyZWRfZ2VuZWNvdW50cyksMTo1NV0KQUlIX3VuZmlsdGVyZWRfZ2VuZWNvdW50cyA8LSBBSUhfdW5maWx0ZXJlZF9nZW5lY291bnRzWzU6bnJvdyhBSUhfdW5maWx0ZXJlZF9nZW5lY291bnRzKSwxOjU1XQoKCmdlbmVuYW1lczwtIHNhcHBseSggc3Ryc3BsaXQoIHJvd25hbWVzKEFJSF9maWx0ZXJlZF9nZW5lY291bnRzKSwgc3BsaXQ9IlxcLiIgKSwgIlsiLCAxICkKcm93bmFtZXMoQUlIX2ZpbHRlcmVkX2dlbmVjb3VudHMpIDwtIGdlbmVuYW1lcwpnZW5lbmFtZXM8LSBzYXBwbHkoIHN0cnNwbGl0KCByb3duYW1lcyhBSUhfdW5maWx0ZXJlZF9nZW5lY291bnRzKSwgc3BsaXQ9IlxcLiIgKSwgIlsiLCAxICkKcm93bmFtZXMoQUlIX3VuZmlsdGVyZWRfZ2VuZWNvdW50cykgPC0gZ2VuZW5hbWVzCgoKY29sbmFtZXMoQUlIX2ZpbHRlcmVkX2dlbmVjb3VudHMpIDwtIHN0cl9leHRyYWN0KGNvbG5hbWVzKEFJSF9maWx0ZXJlZF9nZW5lY291bnRzKSwgcGF0dGVybiA9ICJhYXNsZFsufF9dXFxkKyIpCmNvbG5hbWVzKEFJSF91bmZpbHRlcmVkX2dlbmVjb3VudHMpIDwtIHN0cl9leHRyYWN0KGNvbG5hbWVzKEFJSF91bmZpbHRlcmVkX2dlbmVjb3VudHMpLCBwYXR0ZXJuID0gImFhc2xkWy58X11cXGQrIikKCgoKYGBgCgpUcmFuc2xhdGUgZ2VuZSBuYW1lcyBhbmQgcHVsbCB0aGUgYmlvdHlwZSBhbmQgdGhlIHRyYW5zY3JpcHQgbGVuZ3RoCgpgYGB7cn0KbGlicmFyeShiaW9tYVJ0KQojZW5zZW1ibGUgdXNlIEhHMzggYXMgYSByZWZlcmVuY2UKbGlzdE1hcnRzKGhvc3Q9Ind3dy5lbnNlbWJsLm9yZyIpCm1hcnQgPSB1c2VNYXJ0KGJpb21hcnQ9IkVOU0VNQkxfTUFSVF9FTlNFTUJMIixkYXRhc2V0PSJoc2FwaWVuc19nZW5lX2Vuc2VtYmwiLCBob3N0PSJ3d3cuZW5zZW1ibC5vcmciLCBlbnNlbWJsUmVkaXJlY3QgPSBGQUxTRSkKZ2VuZW1hcCA8LSBnZXRCTSggYXR0cmlidXRlcyA9IGMoImVuc2VtYmxfZ2VuZV9pZCIsICJlbnRyZXpnZW5lIiwgImhnbmNfc3ltYm9sIiwgImdlbmVfYmlvdHlwZSIsICJkZXNjcmlwdGlvbiIsICJ0cmFuc2NyaXB0X2xlbmd0aCIpLAogICAgICAgICAgICAgICAgICBmaWx0ZXJzID0gImVuc2VtYmxfZ2VuZV9pZCIsCiAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGdlbmVuYW1lcywKICAgICAgICAgICAgICAgICAgbWFydCkKCiNWaWV3KGxpc3RBdHRyaWJ1dGVzKG1hcnQpKQoKCmBgYAoKQ3JlYXRlIGEgZ2VuZSBhdHRyaWJ1dGVzIHRhYmxlIApgYGB7cn0KCgojY3JlYXRlIGFuIGluZGV4IGluIHdoaWNoIHlvdSBhcmUgcmVhcnJhbmdpbmcgZW5zbWJsIGdlbmUgaWRzIGZyb20gZ2VuZW1hcCBpbnRvIHNhbWUgb3JkZXIgYXMgdGhleSBhcmUgaW4gZ2VuZW5hbWVzCmlkeCA8LSBtYXRjaChnZW5lbmFtZXMsIGdlbmVtYXAkZW5zZW1ibF9nZW5lX2lkICkKCiNjcmVhdGUgbmV3IGRhdGFzZXQgd2hpY2ggYmluZHMgZ2VuZSBhdHRyaWJ1dGVzCmVudHJleiA8LSBnZW5lbWFwJGVudHJlemdlbmVbIGlkeCBdCmhnbmNfc3ltYm9sIDwtIGdlbmVtYXAkaGduY19zeW1ib2xbIGlkeCBdCmRlc2NyaXB0aW9uIDwtIGdlbmVtYXAkZGVzY3JpcHRpb25bIGlkeCBdCmdlbmVfYmlvdHlwZSA8LSBnZW5lbWFwJGdlbmVfYmlvdHlwZVsgaWR4IF0KZW5zZW1ibCA8LSBnZW5lbWFwJGVuc2VtYmxfZ2VuZV9pZFsgaWR4IF0KdHJhbnNjcmlwdF9sZW5ndGggPC0gZ2VuZW1hcCR0cmFuc2NyaXB0X2xlbmd0aFsgaWR4IF0KCmdhPC1jYmluZChoZ25jX3N5bWJvbCxkZXNjcmlwdGlvbixnZW5lX2Jpb3R5cGUsZW5zZW1ibCxlbnRyZXosIHRyYW5zY3JpcHRfbGVuZ3RoKQoKI21ha2UgZ2EgaW50byBhIGRhdGEgZnJhbWUgZnJvbSBhIG1hdHJpeApnYTwtYXMuZGF0YS5mcmFtZShnYSkKZ2EkZ2VuZV9iaW90eXBlPC1hcy5jaGFyYWN0ZXIoZ2EkZ2VuZV9iaW90eXBlKQp1bmlxdWUoZ2EkZ2VuZV9iaW90eXBlKQoKI1ZpZXcoZ2EgJT4lIGZpbHRlcihnZW5lX2Jpb3R5cGU9PSJtaVJOQSIpKQojVmlldyhnYSAlPiUgZmlsdGVyKGdlbmVfYmlvdHlwZT09Ik10X3JSTkEiKSkKI1ZpZXcoZ2EgJT4lIGZpbHRlcihnZW5lX2Jpb3R5cGU9PSJyUk5BIikpCiNWaWV3KGdhICU+JSBmaWx0ZXIoZ2VuZV9iaW90eXBlPT0iTXRfclJOQSIpKQojVmlldyhnYSAlPiUgZmlsdGVyKGVuc2VtYmw9PSJFTlNHMDAwMDAyNjY2NTgiKSkKI1ZpZXcoZ2EgJT4lIGZpbHRlcihoZ25jX3N5bWJvbD09IlJOQTQ1UzUiKSkKCmBgYAoKQ2FsY3VsYXRlIHRoZSBSUEtNIHVzaW5nIEVkZ2VSCgpgYGB7cn0KI3BjID0gcHJvdGVpbiBjb2RpbmcKcGM8LWdhW2dhJGdlbmVfYmlvdHlwZT09InByb3RlaW5fY29kaW5nIixdCnBjPC1wY1shKGlzLm5hKHBjJGVuc2VtYmwpKSxdCmhlYWQocGMpCgojbWFrZSBhbiBpbmRleCBmb3IgcHJvdGVpbiBjb2RpbmcgZ2VuZXMgYW5kIGNyZWF0ZSBuZXcgZ2VuZSBjb3VudCB0YWJsZXMKaWR4cGM8LSBtYXRjaChwYyRlbnNlbWJsLHJvd25hbWVzKEFJSF9maWx0ZXJlZF9nZW5lY291bnRzKSkKZ2VuZWNvdW50c3BjX0RBU0hlZDwtQUlIX2ZpbHRlcmVkX2dlbmVjb3VudHNbaWR4cGMsXQpyb3duYW1lcyhnZW5lY291bnRzcGNfREFTSGVkKTwtbWFrZS51bmlxdWUoYXMuY2hhcmFjdGVyKHBjJGhnbmNfc3ltYm9sKSkKCmlkeHBjPC0gbWF0Y2gocGMkZW5zZW1ibCxyb3duYW1lcyhBSUhfdW5maWx0ZXJlZF9nZW5lY291bnRzKSkKZ2VuZWNvdW50c3BjX25vREFTSDwtQUlIX3VuZmlsdGVyZWRfZ2VuZWNvdW50c1tpZHhwYyxdCnJvd25hbWVzKGdlbmVjb3VudHNwY19ub0RBU0gpPC1tYWtlLnVuaXF1ZShhcy5jaGFyYWN0ZXIocGMkaGduY19zeW1ib2wpKQoKI3Rha2UgYSBzdWJzZXQgb25seQpnZW5lY291bnRzcGNfREFTSGVkX3N1YnNldCA8LSBnZW5lY291bnRzcGNfREFTSGVkWywxOjldCmdlbmVjb3VudHNwY19ub0RBU0hfc3Vic2V0IDwtIGdlbmVjb3VudHNwY19ub0RBU0hbLDE6OV0KI2dlbmVjb3VudHNwY19EQVNIZWRfc3Vic2V0JGdlbmUgPC0gcm93bmFtZXMoZ2VuZWNvdW50c3BjX0RBU0hlZF9zdWJzZXQpCiNnZW5lY291bnRzcGNfbm9EQVNIX3N1YnNldCRnZW5lIDwtIHJvd25hbWVzKGdlbmVjb3VudHNwY19ub0RBU0hfc3Vic2V0KQoKbGlicmFyeShlZGdlUikKZGdlREFTSGVkIDwtIERHRUxpc3QoY291bnRzID0gZ2VuZWNvdW50c3BjX0RBU0hlZF9zdWJzZXQsIGxpYi5zaXplID0gY29sU3VtcyhnZW5lY291bnRzcGNfREFTSGVkX3N1YnNldCksIGdlbmVzID0gcGMpCmRnZURBU0hlZFJQS00gPC0gcnBrbShkZ2VEQVNIZWQsICBnZW5lLmxlbmd0aCA9IGFzLm51bWVyaWMoZGdlREFTSGVkJGdlbmVzJHRyYW5zY3JpcHRfbGVuZ3RoKSwgbG9nID0gRkFMU0UsIG5vcm1hbGl6ZWQubGliLnNpemVzID0gVFJVRSkKCgoKZGdlbm9EQVNIIDwtIERHRUxpc3QoY291bnRzID0gZ2VuZWNvdW50c3BjX25vREFTSF9zdWJzZXQsIGxpYi5zaXplID0gY29sU3VtcyhnZW5lY291bnRzcGNfbm9EQVNIX3N1YnNldCksIGdlbmVzID0gcGMpCmRnZW5vREFTSFJQS00gPC0gcnBrbShkZ2Vub0RBU0gsICBnZW5lLmxlbmd0aCA9IGFzLm51bWVyaWMoZGdlbm9EQVNIJGdlbmVzJHRyYW5zY3JpcHRfbGVuZ3RoKSwgbG9nID0gRkFMU0UsIG5vcm1hbGl6ZWQubGliLnNpemVzID0gVFJVRSkKCiNtZXJnZSBkYXRhZnJhbWVzCmxpYnJhcnkocmVzaGFwZTIpCmRnZURBU0hlZFJQS01fbWVsdCA8LSBhcy5kYXRhLmZyYW1lKG1lbHQoZGdlREFTSGVkUlBLTSwgdmFybmFtZXMgPSBjKCJnZW5lIiwgInNhbXBsZV9pZCIpLCB2YWx1ZS5uYW1lID0gIkRBU0hlZF9nZW5lX2NvdW50IiApKQpkZ2Vub0RBU0hSUEtNX21lbHQgPC0gYXMuZGF0YS5mcmFtZShtZWx0KGRnZW5vREFTSFJQS00sIHZhcm5hbWVzID0gYygiZ2VuZSIsInNhbXBsZV9pZCIpLCB2YWx1ZS5uYW1lID0gIm5vREFTSF9nZW5lX2NvdW50IiApKQpnZW5lY291bnRzX2Z1bGxfUlBLTSA8LSBkcGx5cjo6bGVmdF9qb2luKGRnZW5vREFTSFJQS01fbWVsdCwgZGdlREFTSGVkUlBLTV9tZWx0LCBieSA9IGMoImdlbmUiLCAic2FtcGxlX2lkIikpCmhlYWQoZ2VuZWNvdW50c19mdWxsX1JQS00pCmBgYAoKQ2FsY3VsYXRlIHRoZSBSUEtNIGJ5IGhhbmQKYGBge3J9CiNwYyA9IHByb3RlaW4gY29kaW5nCnBjPC1nYVtnYSRnZW5lX2Jpb3R5cGU9PSJwcm90ZWluX2NvZGluZyIsXQpwYzwtcGNbIShpcy5uYShwYyRlbnNlbWJsKSksXQpoZWFkKHBjKQoKI21ha2UgYW4gaW5kZXggZm9yIHByb3RlaW4gY29kaW5nIGdlbmVzIGFuZCBjcmVhdGUgbmV3IGdlbmUgY291bnQgdGFibGVzCmlkeHBjPC0gbWF0Y2gocGMkZW5zZW1ibCxyb3duYW1lcyhBSUhfZmlsdGVyZWRfZ2VuZWNvdW50cykpCmdlbmVjb3VudHNwY19EQVNIZWQ8LUFJSF9maWx0ZXJlZF9nZW5lY291bnRzW2lkeHBjLF0Kcm93bmFtZXMoZ2VuZWNvdW50c3BjX0RBU0hlZCk8LW1ha2UudW5pcXVlKGFzLmNoYXJhY3RlcihwYyRoZ25jX3N5bWJvbCkpCgppZHhwYzwtIG1hdGNoKHBjJGVuc2VtYmwscm93bmFtZXMoQUlIX3VuZmlsdGVyZWRfZ2VuZWNvdW50cykpCmdlbmVjb3VudHNwY19ub0RBU0g8LUFJSF91bmZpbHRlcmVkX2dlbmVjb3VudHNbaWR4cGMsXQpyb3duYW1lcyhnZW5lY291bnRzcGNfbm9EQVNIKTwtbWFrZS51bmlxdWUoYXMuY2hhcmFjdGVyKHBjJGhnbmNfc3ltYm9sKSkKCiN0YWtlIGEgc3Vic2V0IG9ubHkKZ2VuZWNvdW50c3BjX0RBU0hlZF9zdWJzZXQgPC0gZ2VuZWNvdW50c3BjX0RBU0hlZFssMTo5XQpnZW5lY291bnRzcGNfbm9EQVNIX3N1YnNldCA8LSBnZW5lY291bnRzcGNfbm9EQVNIWywxOjldCmdlbmVjb3VudHNwY19EQVNIZWRfc3Vic2V0JGdlbmUgPC0gcm93bmFtZXMoZ2VuZWNvdW50c3BjX0RBU0hlZF9zdWJzZXQpCmdlbmVjb3VudHNwY19ub0RBU0hfc3Vic2V0JGdlbmUgPC0gcm93bmFtZXMoZ2VuZWNvdW50c3BjX25vREFTSF9zdWJzZXQpCmdlbmVjb3VudHNwY19EQVNIZWRfc3Vic2V0JHRyYW5zY3JpcHRfbGVuZ3RoIDwtIHBjJHRyYW5zY3JpcHRfbGVuZ3RoCmdlbmVjb3VudHNwY19ub0RBU0hfc3Vic2V0JHRyYW5zY3JpcHRfbGVuZ3RoIDwtIHBjJHRyYW5zY3JpcHRfbGVuZ3RoCmdlbmVjb3VudHNwY19EQVNIZWRfc3Vic2V0X21lbHQgPC0gbWVsdChnZW5lY291bnRzcGNfREFTSGVkX3N1YnNldCwgdmFyaWFibGUubmFtZSA9ICJzYW1wbGVfaWQiLCB2YWx1ZS5uYW1lID0gIkRBU0hlZF9nZW5lX2NvdW50IiApCmdlbmVjb3VudHNwY19ub0RBU0hfc3Vic2V0X21lbHQgPC0gbWVsdChnZW5lY291bnRzcGNfbm9EQVNIX3N1YnNldCwgdmFyaWFibGUubmFtZSA9ICJzYW1wbGVfaWQiLCB2YWx1ZS5uYW1lID0gIm5vREFTSF9nZW5lX2NvdW50IiApCgojam9pbiB0aGUgdHdvIGRhdGFmcmFtZXMgdG9nZXRoZXIKZ2VuZWNvdW50c19mdWxsIDwtIGRwbHlyOjpsZWZ0X2pvaW4oZ2VuZWNvdW50c3BjX0RBU0hlZF9zdWJzZXRfbWVsdCwgZ2VuZWNvdW50c3BjX25vREFTSF9zdWJzZXRfbWVsdCwgYnkgPSBjKCJnZW5lIiwgInNhbXBsZV9pZCIsICJ0cmFuc2NyaXB0X2xlbmd0aCIpKQoKI2NhbGN1bGF0ZSB0aGUgUlBLTSBmb3IgZWFjaCBEQVNIZWQgYW5kIHVuREFTSGVkIGdlbmVjb3VudApnZW5lY291bnRzX2Z1bGxfUlBLTSA8LSBnZW5lY291bnRzX2Z1bGwgJT4lIGRwbHlyOjpncm91cF9ieShzYW1wbGVfaWQpICU+JSBkcGx5cjo6bXV0YXRlKERBU0hlZFJQS00gPSBEQVNIZWRfZ2VuZV9jb3VudCoxMDAwMDAwKjEwMDAvKHN1bShEQVNIZWRfZ2VuZV9jb3VudCkqYXMubnVtZXJpYyh0cmFuc2NyaXB0X2xlbmd0aCkpKSAgJT4lIGRwbHlyOjptdXRhdGUobm9EQVNIUlBLTSA9IG5vREFTSF9nZW5lX2NvdW50KjEwMDAwMDAqMTAwMC8oc3VtKG5vREFTSF9nZW5lX2NvdW50KSphcy5udW1lcmljKHRyYW5zY3JpcHRfbGVuZ3RoKSkpCgpgYGAKCkNhbGN1bGF0aW5nIFJQS00gdXNpbmcgdGhlIGZ1bGwgdG90YWwgcmVhZHMgdmFsdWUgKGZyb20gdGhlIFJlYWRzUGVyR2VuZS50YWIgZmlsZSkKYGBge3J9CiNwYyA9IHByb3RlaW4gY29kaW5nCnBjPC1nYVtnYSRnZW5lX2Jpb3R5cGU9PSJwcm90ZWluX2NvZGluZyIsXQpwYzwtcGNbIShpcy5uYShwYyRlbnNlbWJsKSksXQpoZWFkKHBjKQoKI21ha2UgYW4gaW5kZXggZm9yIHByb3RlaW4gY29kaW5nIGdlbmVzIGFuZCBjcmVhdGUgbmV3IGdlbmUgY291bnQgdGFibGVzCmlkeHBjPC0gbWF0Y2gocGMkZW5zZW1ibCxyb3duYW1lcyhBSUhfZmlsdGVyZWRfZ2VuZWNvdW50cykpCmdlbmVjb3VudHNwY19EQVNIZWQ8LUFJSF9maWx0ZXJlZF9nZW5lY291bnRzW2lkeHBjLF0Kcm93bmFtZXMoZ2VuZWNvdW50c3BjX0RBU0hlZCk8LW1ha2UudW5pcXVlKGFzLmNoYXJhY3RlcihwYyRoZ25jX3N5bWJvbCkpCgppZHhwYzwtIG1hdGNoKHBjJGVuc2VtYmwscm93bmFtZXMoQUlIX3VuZmlsdGVyZWRfZ2VuZWNvdW50cykpCmdlbmVjb3VudHNwY19ub0RBU0g8LUFJSF91bmZpbHRlcmVkX2dlbmVjb3VudHNbaWR4cGMsXQpyb3duYW1lcyhnZW5lY291bnRzcGNfbm9EQVNIKTwtbWFrZS51bmlxdWUoYXMuY2hhcmFjdGVyKHBjJGhnbmNfc3ltYm9sKSkKCiN0YWtlIGEgc3Vic2V0IG9ubHkKZ2VuZWNvdW50c3BjX0RBU0hlZF9zdWJzZXQgPC0gZ2VuZWNvdW50c3BjX0RBU0hlZFssMTo5XQpnZW5lY291bnRzcGNfbm9EQVNIX3N1YnNldCA8LSBnZW5lY291bnRzcGNfbm9EQVNIWywxOjldCmdlbmVjb3VudHNwY19EQVNIZWRfc3Vic2V0JGdlbmUgPC0gcm93bmFtZXMoZ2VuZWNvdW50c3BjX0RBU0hlZF9zdWJzZXQpCmdlbmVjb3VudHNwY19ub0RBU0hfc3Vic2V0JGdlbmUgPC0gcm93bmFtZXMoZ2VuZWNvdW50c3BjX25vREFTSF9zdWJzZXQpCmdlbmVjb3VudHNwY19EQVNIZWRfc3Vic2V0JHRyYW5zY3JpcHRfbGVuZ3RoIDwtIHBjJHRyYW5zY3JpcHRfbGVuZ3RoCmdlbmVjb3VudHNwY19ub0RBU0hfc3Vic2V0JHRyYW5zY3JpcHRfbGVuZ3RoIDwtIHBjJHRyYW5zY3JpcHRfbGVuZ3RoCmdlbmVjb3VudHNwY19EQVNIZWRfc3Vic2V0X21lbHQgPC0gbWVsdChnZW5lY291bnRzcGNfREFTSGVkX3N1YnNldCwgdmFyaWFibGUubmFtZSA9ICJzYW1wbGVfaWQiLCB2YWx1ZS5uYW1lID0gIkRBU0hlZF9nZW5lX2NvdW50IiApCmdlbmVjb3VudHNwY19ub0RBU0hfc3Vic2V0X21lbHQgPC0gbWVsdChnZW5lY291bnRzcGNfbm9EQVNIX3N1YnNldCwgdmFyaWFibGUubmFtZSA9ICJzYW1wbGVfaWQiLCB2YWx1ZS5uYW1lID0gIm5vREFTSF9nZW5lX2NvdW50IiApCgojam9pbiB0aGUgdHdvIGRhdGFmcmFtZXMgdG9nZXRoZXIKZ2VuZWNvdW50c19mdWxsIDwtIGRwbHlyOjpsZWZ0X2pvaW4oZ2VuZWNvdW50c3BjX0RBU0hlZF9zdWJzZXRfbWVsdCwgZ2VuZWNvdW50c3BjX25vREFTSF9zdWJzZXRfbWVsdCwgYnkgPSBjKCJnZW5lIiwgInNhbXBsZV9pZCIsICJ0cmFuc2NyaXB0X2xlbmd0aCIpKQoKI2FkZCB0aGUgdG90YWwgcmVhZHMgKGJlZm9yZSBmaWx0ZXJpbmcgcHJvdGVpbiBjb2RpbmcpIGFuZCBjYWxjdWxhdGUgdGhlIFJQS00KZ2VuZWNvdW50c19mdWxsX1JQS00gPC0gZ2VuZWNvdW50c19mdWxsICU+JSBkcGx5cjo6Z3JvdXBfYnkoc2FtcGxlX2lkKSAlPiUgZHBseXI6Om11dGF0ZShEQVNIZWR0b3RhbHJlYWRzID0gdG90YWxyZWFkc1tzYW1wbGVfaWQsIkRBU0hlZHRvdGFscmVhZHMiXSkgJT4lIGRwbHlyOjptdXRhdGUobm9EQVNIdG90YWxyZWFkcyA9IHRvdGFscmVhZHNbc2FtcGxlX2lkLCJub0RBU0h0b3RhbHJlYWRzIl0pICU+JSBkcGx5cjo6bXV0YXRlKERBU0hlZFJQS00gPSBEQVNIZWRfZ2VuZV9jb3VudCoxMDAwMDAwKjEwMDAvKERBU0hlZHRvdGFscmVhZHMqYXMubnVtZXJpYyh0cmFuc2NyaXB0X2xlbmd0aCkpKSAgJT4lIGRwbHlyOjptdXRhdGUobm9EQVNIUlBLTSA9IG5vREFTSF9nZW5lX2NvdW50KjEwMDAwMDAqMTAwMC8obm9EQVNIdG90YWxyZWFkcyphcy5udW1lcmljKHRyYW5zY3JpcHRfbGVuZ3RoKSkpCgoKYGBgCgoKYGBge3J9CgojY3JlYXRlIGEgc3VtbWFyeSB0YWJsZQpnZW5lY291bnRzX2Z1bGxfUlBLTV9zdW1tYXJ5IDwtIGdlbmVjb3VudHNfZnVsbF9SUEtNICU+JSBkcGx5cjo6Z3JvdXBfYnkoc2FtcGxlX2lkKSAlPiUgZHBseXI6OnN1bW1hcmlzZShyMiA9IHNpZ25pZihzdW1tYXJ5KGxtKERBU0hlZFJQS00gfiBub0RBU0hSUEtNKSkkYWRqLnIuc3F1YXJlZCwgZGlnaXRzID0gNSksIHNsb3BlID0gc2lnbmlmKGxtKERBU0hlZFJQS00gfiBub0RBU0hSUEtNKSRjb2VmW1syXV0sIGRpZ2l0cyA9IDMpLCBpbnRlcmNlcHQgPSBzaWduaWYobG0oREFTSGVkUlBLTSB+IG5vREFTSFJQS00pJGNvZWZbWzFdXSwgZGlnaXRzID0gMykpCgojY3JlYXRlIGZhY2V0IGxhYmVscwpuYW1lczwtIGxhcHBseShnZW5lY291bnRzX2Z1bGxfUlBLTV9zdW1tYXJ5LCBhcy5jaGFyYWN0ZXIpCmZhY2V0X3RpdGxlcyA8LSBjKCkKCmZvciAoaSBpbiAxOm5yb3coZ2VuZWNvdW50c19mdWxsX1JQS01fc3VtbWFyeSkpIHsKICBmYWNldF90aXRsZXNbaV0gPC0gcGFzdGUobmFtZXMkc2FtcGxlX2lkW2ldLCJcblIyID0iLCBuYW1lcyRyMltpXSwiICB5ID0gIiwgbmFtZXMkc2xvcGVbaV0sICJ4ICsgIiwgbmFtZXMkaW50ZXJjZXB0W2ldKQp9CgpmYWNldF90aXRsZXMKCmZhY2V0X2xhYmVsbGVyIDwtIGZ1bmN0aW9uKHZhcmlhYmxlLHZhbHVlKXsKICByZXR1cm4oZmFjZXRfdGl0bGVzW3ZhbHVlXSkKfQoKCgoKY29sbmFtZXMoZ2VuZWNvdW50c19mdWxsX1JQS00pCmdncGxvdChkYXRhID0gZ2VuZWNvdW50c19mdWxsX1JQS00sIGFlcyh4PSBub0RBU0hSUEtNLCB5ID0gREFTSGVkUlBLTSkpICsgZ2VvbV9wb2ludChjb2xvciA9ICJkYXJrZ3JlZW4iLCBhbHBoYSA9IDAuNSkgKyBmYWNldF93cmFwKH5zYW1wbGVfaWQsIGxhYmVsbGVyID0gZmFjZXRfbGFiZWxsZXIpICsgc3RhdF9zbW9vdGgobWV0aG9kID0gImxtIiwgY29sb3IgPSAicmVkIikgKyB4bGFiICgidW5maWx0ZXJlZCAoUlBLTS9GUEtNKSIpICsgeWxhYiAoImZpbHQtODUtOTkuOS05MCBnZW5lIGNvdW50cyAoUlBLTS9GUEtNKSIpICsgc2NhbGVfeF9sb2cxMCgpICsgc2NhbGVfeV9sb2cxMCgpCmBgYAoKRmlsdGVyIG91dCBSUEtNIHdpdGggMCB2YWx1ZXMKYGBge3J9CiNjcmVhdGUgZmlsdGVyZWQgdGFibGUKZ2VuZWNvdW50c19mdWxsX1JQS01fZmlsdGVyZWQgPC0gZ2VuZWNvdW50c19mdWxsX1JQS00gICU+JSBncm91cF9ieShzYW1wbGVfaWQsIGdlbmUpICU+JSBmaWx0ZXIoREFTSGVkUlBLTSA+IDAgJiYgbm9EQVNIUlBLTSA+IDApCgojY3JlYXRlIGEgc3VtbWFyeSB0YWJsZQpnZW5lY291bnRzX2Z1bGxfUlBLTV9zdW1tYXJ5X2ZpbHRlcmVkIDwtIGdlbmVjb3VudHNfZnVsbF9SUEtNX2ZpbHRlcmVkICU+JSBkcGx5cjo6Z3JvdXBfYnkoc2FtcGxlX2lkKSAlPiUgZHBseXI6OnN1bW1hcmlzZShyMiA9IHNpZ25pZihzdW1tYXJ5KGxtKERBU0hlZFJQS00gfiBub0RBU0hSUEtNKSkkYWRqLnIuc3F1YXJlZCwgZGlnaXRzID0gNSksIHNsb3BlID0gc2lnbmlmKGxtKERBU0hlZFJQS00gfiBub0RBU0hSUEtNKSRjb2VmW1syXV0sIGRpZ2l0cyA9IDMpLCBpbnRlcmNlcHQgPSBzaWduaWYobG0oREFTSGVkUlBLTSB+IG5vREFTSFJQS00pJGNvZWZbWzFdXSwgZGlnaXRzID0gMykpCgojY3JlYXRlIGZhY2V0IGxhYmVscwpuYW1lczwtIGxhcHBseShnZW5lY291bnRzX2Z1bGxfUlBLTV9zdW1tYXJ5X2ZpbHRlcmVkLCBhcy5jaGFyYWN0ZXIpCgpmb3IgKGkgaW4gMTpucm93KGdlbmVjb3VudHNfZnVsbF9SUEtNX3N1bW1hcnlfZmlsdGVyZWQpKSB7CiAgZmFjZXRfdGl0bGVzW2ldIDwtIHBhc3RlKG5hbWVzJHNhbXBsZV9pZFtpXSwiXG5SMiA9IiwgbmFtZXMkcjJbaV0sIiAgeSA9ICIsIG5hbWVzJHNsb3BlW2ldLCAieCArICIsIG5hbWVzJGludGVyY2VwdFtpXSkKfQoKZmFjZXRfdGl0bGVzCgpmYWNldF9sYWJlbGxlciA8LSBmdW5jdGlvbih2YXJpYWJsZSx2YWx1ZSl7CiAgcmV0dXJuKGZhY2V0X3RpdGxlc1t2YWx1ZV0pCn0KCgoKVmlldyhnZW5lY291bnRzX2Z1bGxfUlBLTV9maWx0ZXJlZCkKZ2dwbG90KGRhdGEgPSBnZW5lY291bnRzX2Z1bGxfUlBLTV9maWx0ZXJlZCwgYWVzKHg9IG5vREFTSFJQS00sIHkgPSBEQVNIZWRSUEtNKSkgKyBnZW9tX3BvaW50KGNvbG9yID0gImRhcmtncmVlbiIsIGFscGhhID0gMC41KSArIGZhY2V0X3dyYXAofnNhbXBsZV9pZCwgbGFiZWxsZXIgPSBmYWNldF9sYWJlbGxlcikgKyBzdGF0X3Ntb290aChtZXRob2QgPSAibG0iLCBjb2xvciA9ICJyZWQiKSArIHhsYWIgKCJ1bmZpbHRlcmVkIChSUEtNL0ZQS00pIikgKyB5bGFiICgiZmlsdC04NS05OS45LTkwIGdlbmUgY291bnRzIChSUEtNL0ZQS00pIikgKyBzY2FsZV94X2xvZzEwKCkgKyBzY2FsZV95X2xvZzEwKCkKYGBg